<?php
abstract class CF_Storage_Abstract implements Countable, Iterator
{
	/**
	 * 
	 * @var array
	 */
	protected $_data = array();
	
	/**
	 * 
	 * @var boolean
	 */
	protected $_readOnly = true;
	
	/**
	 * 
	 * @var boolean
	 */
	protected $_overrideOnExist = true;
	
	/**
	 * 
	 * @param mixed $id
	 * @return boolean
	 */
	public function has($id)
	{
		return isset($this->_data[$id]);	
	}
	
	/**
	 * 
	 * @param mixed $id
	 * @param mixed $default
	 * @return mixed
	 */
	public function get($id, $default = null)
	{
		return $this->has($id)? $this->_data[$id] : $default;	
	}
	
	/**
	 * 
	 * @param mixed $id
	 * @param mixed $value
	 * @return CF_Storage_Abstract
	 */
	public function set($id, $value)
	{
		# check if allow write
		if($this->_readOnly){
			throw new CF_Storage_Exception('Data has been marked read-only');
		}
		
		# check if allow override
		if(!$this->_overrideOnExist) return $this;
		
		# set data
		$this->_data[$id] = $value;
		
		return $this;
	}
	
	/**
	 * 
	 * @param mixed $id
	 * @return CF_Storage_Abstract
	 */
	public function remove($id)
	{
		# check if allow write
		if($this->_readOnly){
			throw new CF_Session_Exception('Data has been marked read-only');
		}
		
		unset($this->_data[$id]);
		
		return $this;
	}
	
	/**
	 * 
	 * @param mixed $id
	 * @return boolean
	 */
	public function __isset($id)
	{
		return $this->has($id);
	}
	
	/**
	 * 
	 * @param mixed $id
	 * @return mixed
	 */
	public function __get($id)
	{
		return $this->get($id);
	}
	
	/**
	 * 
	 * @param mixed $id
	 * @param mixed $value
	 * @return mixed
	 */
	public function __set($id, $value)
	{
		return $this->set($id, $value);
	}
	
	/**
	 * 
	 * @param mixed $id
	 * @return mixed
	 */
	public function __unset($id)
	{
		return $this->remove($id);
	}
	
	/**
	 * @return int
	 */
	public function count()
	{
		return count($this->_data);
	}
	
	/**
	 * @return mixed
	 */
	public function current()
	{
		return current($this->_data);
	}
	
	/**
	 * @return CF_Storage_Abstract
	 */
	public function next()
	{
		next($this->_data);
		return $this;
	}
	
	/**
	 * @return CF_Storage_Abstract
	 */
	public function rewind()
	{
		reset($this->_data);
		return $this;
	}
	
	/**
	 * @return mixed
	 */
	public function key()
	{
		return key($this->_data);
	}
	
	/**
	 * @return boolean
	 */
	public function valid()
	{
		return null !== $this->key();
	}
	
	/**
	 * @return array
	 */
	public function toArray()
	{
		$data = array();
		foreach($this->_data as $k => $v){
			$data[$k] = ($v instanceof CF_Storage_Abstract)? $v->toArray() : $v;
		}
		
		return $data;
	}
	
	/**
	 * @return boolean
	 */
	public function isEmpty()
	{
		return empty($this->_data);
	}
	
	/**
	 * @return array
	 */
	public function getData()
	{
		return $this->_data;
	}
	
	/**
	 * @return CF_Storage_Abstract $this
	 */
	public function reset()
	{
		$this->_data = array();
		return $this;
	}
}